package com.qs.sckill.service.impl;

import com.qs.sckill.config.redis.key.GoodsKey;
import com.qs.sckill.config.redis.key.SckillOrderKey;
import com.qs.sckill.dao.OrderDao;
import com.qs.sckill.dao.SckillGoodsDao;
import com.qs.sckill.dao.SckillOrderDao;
import com.qs.sckill.entity.OrderInfo;
import com.qs.sckill.entity.SckillGoods;
import com.qs.sckill.entity.SckillOrder;
import com.qs.sckill.entity.User;
import com.qs.sckill.service.OrderService;
import com.qs.sckill.service.RedisService;
import com.qs.sckill.utils.UUIDUtil;
import com.qs.sckill.vo.GoodsVo;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Service(value = "orderService")
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private SckillOrderDao sckillOrderDao;

    @Autowired
    private SckillGoodsDao sckillGoodsDao;

    @Autowired
    private RedisService redisService;

    /**
     * 根据用户和商品查询是否存在秒杀订单，如果存在则返回订单。（先从redis中获取，不存在在查询数据库）
     */
    @Override
    public SckillOrder getSckillOrderByUserIdAndGoodsId(String userId, String goodsId) {
        String key = userId + "_" + goodsId;
        SckillOrder sckillOrder = redisService.get(SckillOrderKey.sckillOrder, key, SckillOrder.class);
        if (sckillOrder != null) {
            System.out.println("从缓存中获取秒杀订单。。");
            return sckillOrder;
        }

        return sckillOrderDao.getSckillOrderByUserIdAndGoodsId(userId, goodsId);
    }

    /**
     * 备注：在生成订单和秒杀订单时，多个线程同时访问可能会插入多个秒杀订单数据，这里在数据库中t_sckill_order表中user_id、goods_id
     * 添加唯一性约束，代码中指定出现异常回滚，可以保证在并发环境下数据库数据准确性；
     * （注意：service接口中不用捕获异常，会导致数据无法回滚）
     */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = {Exception.class})
    public OrderInfo createOrder(User user, GoodsVo goods) {
        OrderInfo orderInfo = null;
        //1、添加订单
        orderInfo = new OrderInfo();
        String orderId = UUIDUtil.uuid();
        orderInfo.setId(orderId);
        orderInfo.setGoodsId(goods.getId());
        orderInfo.setUserId(user.getId());
        //秒杀商品数量
        orderInfo.setGoodsCount(1);
        //秒杀价格
        orderInfo.setGoodsPrice(goods.getSckillPrice());
        orderInfo.setGoodsName(goods.getGoodsName());
        orderInfo.setOrderChannel(1);
        orderInfo.setStatus("0");
        orderInfo.setCreateDate(new Date());
        orderDao.insert(orderInfo);

        //2、添加秒杀订单
        SckillOrder sckillOrder = new SckillOrder();
        sckillOrder.setId(UUIDUtil.uuid());
        sckillOrder.setOrderId(orderId);
        sckillOrder.setGoodsId(goods.getId());
        sckillOrder.setUserId(user.getId());
        sckillOrderDao.insertSckillOrder(sckillOrder);

        //将秒杀订单保存在redis中，设置失效时间为10分钟
        String key = user.getId() + "_" + goods.getId();
        redisService.set(SckillOrderKey.sckillOrder, key, sckillOrder);
        return orderInfo;
    }

    /**
     * 获取秒杀的结果：
     * 如果消息队列中的用户秒杀请求已经被成功处理，则可以获取到秒杀订单，则表示秒杀成功，返回订单id；
     * 如果因为库存不足，造成用户并没有秒杀到，则直接返回"-1"；
     * <p>
     * 如果库存足够，秒杀没有成功，持续时间久了，则考虑可能是RabbitMQ消息队列堵塞等，则返回"0"，这种状况为异常状态！
     */
    @Override
    public String getGoodsSckillResult(String userId, String goodsId) {
        SckillOrder sckillOrder = getSckillOrderByUserIdAndGoodsId(userId, goodsId);
        if (sckillOrder != null) {
            return sckillOrder.getOrderId();
        } else {
            //库存已经秒杀完了
            if (BooleanUtils.isTrue(isSckillOver(goodsId))) {
                return "-1";
            } else {
                SckillGoods sckillGoods = sckillGoodsDao.getSckillGoodsByGoodsId(goodsId);
                if (sckillGoods.getStockCount() <= 0) {
                    return "-1";
                }

                //继续等待RabbitMQ接收处理？
                return "0";
            }
        }
    }

    @Override
    public OrderInfo getOrderById(String orderId) {
        return orderDao.getOrderById(orderId);
    }

    //redis中是否存在库存已经清空的记录
    private boolean isSckillOver(String goodsId) {
        return redisService.exists(GoodsKey.sckillOver, goodsId);
    }
}
